# -*-coding:utf-8-*-

'''
rabbitmqctl add_user <USERNAME> <PASSWORD>
rabbitmqctl set_user_tags <USERNAME> administrator
rabbitmqctl set_permissions -p / <USERNAME> ".*" ".*" ".*"

注意使用此版本 rabbitmq-server: 3.6.6
链接: https://dl.bintray.com/rabbitmq/rabbitmq-server-deb/:rabbitmq-server_3.6.6-1_all.deb
'''

import paho.mqtt.client as mqtt
from tornado.ioloop import IOLoop, PeriodicCallback
import functools
import json
import motor
import sys
from bson.objectid import ObjectId
from handlers.base import Mqtonado
import settings
import pymongo
import time
from utils.http import http_get, http_post, http_put, http_delete
import settings
import traceback
from tornado.options import define, options
import fcntl, os, io, signal
import pymongo
import logging
import traceback
from tornado.gen import coroutine

define('prod', default=True, help='Run in product mode or not', type=bool)
options.parse_command_line()
logger = logging.getLogger()
if options.prod:
    logger.setLevel(logging.INFO)
else:
    logger.setLevel(logging.DEBUG)

MID_KEY_MAP = {}
MSG_QUE_MAP = {}

'''
设备 offline 通知
topic: "t/sys/<GROUP_ID>/fw"
{
    "type": "offline" | "online",
    "device_id": "DEVICE_ID",
}

门禁呼叫记录
'''


# The callback for when a PUBLISH message is received from the server.
def on_message(client, userdata, msg):
    print(msg.topic + " " + str(msg.payload))

    try:
        message = json.loads(msg.payload)

        if message['type'] == 'offline':
            data = message['data']
            device_id = data['device_id']
            if MSG_QUE_MAP.has_key(device_id):
                MSG_QUE_MAP[device_id].append(0)
            else:
                MSG_QUE_MAP[device_id] = [0]
            # update online state of device
            # FW message: pub f/dev/{device_id}

            '''
            Returns a MQTTMessageInfo class, which can be used to determine whether
        the message has been delivered (using info.is_published()) or to block
        waiting for the message to be delivered (info.wait_for_publish()). The
        message ID and return code of the publish() call can be found at
        info.mid and info.rc.
        '''
            info = client.publish('f/dev/%s' % device_id, msg.payload)
            MID_KEY_MAP[info.mid] = device_id
            if MSG_QUE_MAP.has_key(device_id):
                MSG_QUE_MAP[device_id].append(0)
            else:
                MSG_QUE_MAP[device_id] = [0]
        else:
            pass
    except Exception as e:
        print e


# client.connect_async("localhost", 1883, 60)
# Blocking call that processes network traffic, dispatches callbacks and
# handles reconnecting.
# Other loop*() functions are available that give a threaded interface and a
# manual interface.
# client.loop_forever()


RPC_REQ_TOPIC = 't/sys'
RPC_RES_TOPIC = 'f/sys/resp/%s'

'''
n-d-s-a: t/s/{gid}/w/f/d/{did}/n/{NOTIFY_NAME} - 9 part
r-d-s-a: t/s/{gid}/w/f/d/{did}/q/{rid}/{REQUEST_NAME} - 10 part
         t/s/{gid}/w/t/d/{did}/p/{rid} - 9 part
r-d-s:   t/s/{gid}/q/{rid}/{REQUEST_NAME}/h/t/d/{did}/p - 11 part
'''

GROUP_ID = 8
# sys 转发(w) dev 的 notify
MAIN_TOPIC = 't/s/%d/#' % GROUP_ID
NOTIFY_TOPIC = 't/s/%d/w/f/d/#' % GROUP_ID
# sys 处理(h) dev 的 request
# REQUEST_TOPIC = 't/s/%d/q/%s/h/t/d/#' % GROUP_ID
NOTIFY_TOPIC_ELE_SET = set(['t', 's', 'w', 'f', 'd', 'n'])
RDS_TOPIC_ELE_SET = set(['t', 's', 'q', 'h', 't', 'd', 'p'])


@coroutine
def handle_notification_dev_sys_app(client, device_id, notify_name, payload, topic_fw):
    is_standard_notify = True
    modification = {}
    if payload is not None:
        try:
            payload_obj = json.loads(payload)
        except:
            payload_obj = None
    url = 'http://%s/internal/api/devices/%s' % (settings.MQ_HTTP_GW_HOST, device_id)
    if 'online' == notify_name:
        modification['online_at'] = int(time.time())
    elif 'offline' == notify_name:
        modification['offline_at'] = int(time.time())
    elif 'coordinates' == notify_name:
        modification['coordinates'] = payload_obj
    else:
        modification['online_at'] = int(time.time())
        response = yield http_put(url, modification)
        logger.info(response)

        is_standard_notify = False
        if 'wpm_usage' == notify_name:
            url = 'http://%s/tmp/api/devices/%s/statistics/water' % (settings.MQ_HTTP_GW_HOST, device_id)
            data = [0, 0, 0, 0, payload_obj['tm']]
            if 'val' in payload_obj:
                data[payload_obj['tp']] = payload_obj['val']
            else:
                data[payload_obj['tp']] = 1
            body = {
                'data': data,
            }
            response = yield http_post(url, body)
            logger.info(response)
        else:
            url = 'http://%s/internal/api/devices/%s/specifics/%s' % (settings.MQ_HTTP_GW_HOST, device_id, notify_name)
            response = yield http_put(url, payload_obj)
            logger.info(response)

    if is_standard_notify:
        response = yield http_put(url, modification)
        logger.info(response)

    client.publish(topic_fw, payload)


def handle_request_dev_sys_app():
    pass


@coroutine
def handle_request_dev_sys(client, device_id, request_name, payload, topic_fw):
    is_standard_notify = True
    if payload is not None:
        try:
            payload_obj = json.loads(payload)
        except:
            payload_obj = None
    if 'wpm_quota' == request_name:
        try:
            url = 'http://%s/internal/api/devices/%s/specifics/%s' % (settings.MQ_HTTP_GW_HOST, device_id, request_name)
            response = yield http_get(url)
            response = json.loads(response.body)
            response = response['data']
            logger.info(response)
            result = {}
            # test
            # response = {'mode': 4, 'expire_time': int(time.time() + 3)}
            # response = {'mode': 0, 'remain': 10, 'used': 9}
            if 'mode' in response:
                result['mode'] = int(response['mode'])
                if 4 == result['mode']:  # 按时间
                    remain = int(response['expire_time']) - int(time.time())
                    if 0 >= remain:
                        result['exceeded'] = 1
                    else:
                        result['exceeded'] = 0
                        result['remain'] = remain
                elif 8 == result['mode']:  # 按流量
                    remain = int(response['remain']) - int(response['used'])
                    if 0 >= remain:
                        result['exceeded'] = 1
                    else:
                        result['exceeded'] = 0
                        result['remain'] = remain
            logger.debug(result)
            result = json.dumps(result)
            client.publish(topic_fw, result)
        except:
            client.publish(topic_fw, traceback.format_exc())
    elif 'power_on' == request_name:
        url = 'http://%s/internal/api/devices/%s' % (settings.MQ_HTTP_GW_HOST, device_id)
        response = yield http_get(url)
        response = json.loads(response.body)
        response = response['data']
        if 'power_on' not in response:
            response['power_on'] = True
        result = dict(power_on=response['power_on'])
        logger.debug(result)
        result = json.dumps(result)
        client.publish(topic_fw, result)
    elif 'enabled' == request_name:
        url = 'http://%s/internal/api/devices/%s' % (settings.MQ_HTTP_GW_HOST, device_id)
        response = yield http_get(url)
        response = json.loads(response.body)
        response = response['data']
        if 'enabled' not in response:
            response['enabled'] = True
        result = dict(enabled=response['enabled'])
        result = json.dumps(result)
        client.publish(topic_fw, result)


@coroutine
def on_message_new_without_try_catch(client, userdata, topic, payload):
    logger.info('%s: %s' % (topic, payload))
    topic_splited = topic.split('/')
    logger.info('%s, %s' % (NOTIFY_TOPIC_ELE_SET, topic_splited))
    if NOTIFY_TOPIC_ELE_SET & set(topic_splited) == NOTIFY_TOPIC_ELE_SET and 9 == len(topic_splited):
        device_id = topic_splited[-3]
        notify_name = topic_splited[-1]
        topic_fw = '/'.join(topic_splited[4:])
        logger.info(topic_fw)
        yield handle_notification_dev_sys_app(client, device_id, notify_name, payload, topic_fw)
    elif RDS_TOPIC_ELE_SET & set(topic_splited) == RDS_TOPIC_ELE_SET and 11 == len(topic_splited):
        # r-d-s:   t/s/{gid}/q/{rid}/{REQUEST_NAME}/h/t/d/{did}/p - 11 part
        device_id = topic_splited[-2]
        request_name = topic_splited[-6]
        topic_fw = '/'.join(topic_splited[-4:])
        rid = topic_splited[4]
        topic_fw += '/%s' % rid
        logger.info('%s, %s, %s' % (topic_fw, device_id, request_name))
        yield handle_request_dev_sys(client, device_id, request_name, payload, topic_fw)


@coroutine
def on_message_new(client, userdata, topic, payload):
    try:
        yield on_message_new_without_try_catch(client, userdata, topic, payload)
    except:
        logger.error(traceback.format_exc())


def main():
    client = Mqtonado(on_message_new, None)
    client.connect(settings.MQ_HOST, username=settings.MQ_USER, password=settings.MQ_PASS)
    client.subscribe(MAIN_TOPIC)
    IOLoop.current().start()


if '__main__' == __name__:
    main()
